package com.fowo.api.sys.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fowo.api.common.model.RException;
import com.fowo.api.sys.entity.SysMenu;
import com.fowo.api.sys.mapper.SysMenuMapper;
import com.fowo.api.sys.model.CreateSysMenu;
import com.fowo.api.sys.model.SysMenuBatchSetAppPo;
import com.fowo.api.sys.model.SysMenuSearch;
import com.fowo.api.sys.model.SysMenuVo;
import com.fowo.api.sys.service.SysMenuService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {

    @Transactional
    @Override
    public SysMenu create(CreateSysMenu model) throws RException {
        SysMenu sysMenu = new SysMenu();
        BeanUtils.copyProperties(model, sysMenu);
        sysMenu.doCreate();
        if(save(sysMenu)) {
            return sysMenu;
        }
        return null;
    }

    @Override
    public List<SysMenuVo> search(SysMenuSearch search) {
        List<SysMenuVo> sysMenuVos = getBaseMapper().search(search);
        return fillSysMenuTree(sysMenuVos);
    }

    @Override
    public List<SysMenuVo> getUserMenuTree(Long userId) {
        List<SysMenuVo> sysMenuVos = getBaseMapper().searchByUserId(userId);
        return fillSysMenuTree(sysMenuVos);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void batchSetApp(SysMenuBatchSetAppPo menuBatchSetAppPo) {
        List<SysMenu> menus = getBaseMapper().selectList(Wrappers.<SysMenu>query()
                .select("id", "app_ids")
                .in("id", menuBatchSetAppPo.getMenuIds())
        );
        if (menus.isEmpty()) {
            return;
        }
        for (SysMenu menu : menus) {
            List<Long> appIds = menu.getAppIds();
            if (appIds == null) {
                appIds = new ArrayList<>();
                menu.setAppIds(appIds);
            }
            if(appListChange(appIds, menuBatchSetAppPo.getType(), menuBatchSetAppPo.getAppIds())) {
                new UpdateChainWrapper<SysMenu>(baseMapper)
                        .eq("id", menu.getId())
                        .set(!appIds.isEmpty(), "app_ids", JSON.toJSONString(appIds))
                        .setSql(appIds.isEmpty(), "app_ids = null")
                        .update();
            }
        }
    }

    private boolean appListChange(List<Long> appIds, String type, List<Long> actionAppIds) {
        if (SysMenuBatchSetAppPo.TYPE_SET.equals(type)) {
            if (appIds.size() != actionAppIds.size()) {
                appIds.clear();
                appIds.addAll(actionAppIds);
                return true;
            }
            List<Long> onlyAdd = new ArrayList<>(actionAppIds);
            onlyAdd.removeAll(appIds);
            if (!onlyAdd.isEmpty()) {
                appIds.clear();
                appIds.addAll(actionAppIds);
                return true;
            }
        }
        else if (SysMenuBatchSetAppPo.TYPE_ADD.equals(type)) {
            actionAppIds.removeAll(appIds);
            if (!actionAppIds.isEmpty()) {
                appIds.addAll(actionAppIds);
                return true;
            }
        }
        else if (SysMenuBatchSetAppPo.TYPE_REMOVE.equals(type)) {
            int size = appIds.size();
            appIds.removeAll(actionAppIds);
            return size != appIds.size();
        }
        return false;
    }

    private List<SysMenuVo> fillSysMenuTree(List<SysMenuVo> sysMenuVos) {
        List<SysMenuVo> root = new ArrayList<>();
        fillSysMenuTree(sysMenuVos, root, null);
        return root;
    }

    private void fillSysMenuTree(List<SysMenuVo> all, List<SysMenuVo> children, Long parentId) {
        for (SysMenuVo menuVo : all) {
            if (Objects.equals(menuVo.getParentMenuId(), parentId)) {
                children.add(menuVo);
                menuVo.setChildren(new ArrayList<>());
                fillSysMenuTree(all, menuVo.getChildren(), menuVo.getId());
            }
        }
    }

    private List<Long> allIdByParentIds(List<Long> ids) {
        if (ids.isEmpty()) {
            return Collections.emptyList();
        }
        // 取所有下级 id
        List<Long> childIds = list(Wrappers.<SysMenu>query()
                .select("id")
                .eq("del_flag", false)
                .in("parent_menu_id", ids))
                .stream().map(SysMenu::getId).collect(Collectors.toList());
        if (childIds.isEmpty()) {
            return Collections.emptyList();
        }
        childIds.addAll(allIdByParentIds(childIds));
        return childIds;
    }

    @Override
    public boolean removeById(Serializable id) {
        SysMenu sysMenu = getById(id);
        if(sysMenu != null && sysMenu.isProtected()) {
            // 被保护的数据不能删除
            return false;
        }
        // 处理下级删除
        List<Long> childIds = allIdByParentIds(Collections.singletonList((Long)id));
        if (!childIds.isEmpty()) {
            super.removeByIds(childIds);
        }
        return super.removeById(id);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean removeByIds(Collection<?> list) {
        boolean allFail = true;
        for(Object id : list) {
            if (removeById((Serializable)id)) {
                allFail = false;
            }
        }
        return !allFail;
    }
}
